package org.monkey.common.constant.util;

import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * protostuff 序列化工具类，基于protobuf封装
 */
public class ProtostuffUtil {

  private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();

  private static <T> Schema<T> getSchema(Class<T> clazz) {
    @SuppressWarnings("unchecked")
    Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);
    if (schema == null) {
      schema = RuntimeSchema.getSchema(clazz);
      if (schema != null) {
        cachedSchema.put(clazz, schema);
      }
    }
    return schema;
  }

  /**
   * 序列化
   *
   * @param obj
   * @return
   */
  public static <T> byte[] serialize(T obj) {
    @SuppressWarnings("unchecked")
    Class<T> clazz = (Class<T>) obj.getClass();
    LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
    try {
      Schema<T> schema = getSchema(clazz);
      return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
    } catch (Exception e) {
      throw new IllegalStateException(e.getMessage(), e);
    } finally {
      buffer.clear();
    }
  }

  /**
   * 序列化
   *
   * @param obj
   * @return
   */
  public static <T> String serializeStr(T obj) {
    byte[] bytes = serialize(obj);
    return new String(Base64.getEncoder().encode(bytes), StandardCharsets.UTF_8);
  }

  public static <T> T deserializeStr(String str, Class<T> clazz) {
    byte[] bytes = Base64.getDecoder().decode(str.getBytes(StandardCharsets.UTF_8));
    return deserialize(bytes, clazz);
  }

  /**
   * 反序列化
   *
   * @param data
   * @param clazz
   * @return
   */
  public static <T> T deserialize(byte[] data, Class<T> clazz) {
    try {
      T obj = clazz.newInstance();
      Schema<T> schema = getSchema(clazz);
      ProtostuffIOUtil.mergeFrom(data, obj, schema);
      return obj;
    } catch (Exception e) {
      throw new IllegalStateException(e.getMessage(), e);
    }
  }
}

